#ifndef __LOGGER_H__
#define __LOGGER_H__

#include "Spark/Utils/NonCopyable.h"
#include "Spark/Net/CallbackDefs.h"
#include "Spark/Utils/LogStream.h"
#include "Spark/Utils/TimePoint.h"
#include <vector>
#include <memory>
#include <string.h>
#include <stdio.h>

namespace wb
{
class Logger : public NonCopyable
{
  public:
    enum class LogLevel : int
    {
        kTrace = 0,
        kDebug,
        kInfo,
        kWarn,
        kError,
        kFatal,
        kNumberOfLogLevels
    };

    /**
     * @brief Nested helper class to calculate filename at compile time
     */
    struct SourceFile
    {
        template <int N>
        inline SourceFile(const char (&arr)[N]) : data_(arr), size_(N - 1)
        {
            // std::cout<<data_<<std::endl;
            const char *slash = strrchr(data_, '/');  // builtin function
            if (slash)
            {
                data_ = slash + 1;
                size_ -= static_cast<int>(data_ - arr);
            }
        }

        explicit SourceFile(const char *filename) : data_(filename)
        {
            const char *slash = strrchr(filename, '/');
            if (slash)
                data_ = slash + 1;
            size_ = static_cast<int>(strlen(data_));
        }

        const char *data_;
        int size_;
    };
    Logger(SourceFile file, int line);
    Logger(SourceFile file, int line, LogLevel level);
    Logger(SourceFile file, int line, bool isSysErr);
    Logger(SourceFile file, int line, LogLevel level, const char *func);
    ~Logger();

    LogStream &stream();

    static void setOutputFunction(OutputFunc &&outputFunc,
                                  FlushFunc &&flushFunc)
    {
        outputFunc_ = std::move(outputFunc);
        flushFunc_ = std::move(flushFunc);
    }

    static void setLogLevel(LogLevel level)
    {
        logLevel_() = level;
    }

    /**
     * @brief Get the current log level.
     *
     * @return LogLevel
     */
    static LogLevel currentLevel()
    {
        return logLevel_();
    }

  protected:
    static void defaultOutputFunction(const char *msg, const uint64_t len)
    {
        fwrite(msg, 1, len, stdout);
    }
    static void defaultFlushFunction()
    {
        fflush(stdout);
    }

    void formatTime();

    static LogLevel &logLevel_()
    {
#ifdef RELEASE
        static LogLevel logLevel = LogLevel::kInfo;
#else
        static LogLevel logLevel = LogLevel::kDebug;
#endif
        return logLevel;
    }

    static void newOutputFunc_(OutputFunc &&newOutputFunc)
    {
        outputFunc_ = std::move(newOutputFunc);
    }

    static void newFlushFunc_(FlushFunc &&newFlushFunc)
    {
        flushFunc_ = std::move(newFlushFunc);
    }
    LogStream logStream_;
    TimePoint date_{};
    SourceFile sourceFile_;
    int fileLine_;
    LogLevel level_;
    static FlushFunc flushFunc_;
    static OutputFunc outputFunc_;
};

}  // namespace wb

/**
 * @brief Thread local errno strerror wrapper
 * @param savedErrno
 * @return const char*
 */
const char *strerror_tl(int savedErrno);

#ifdef NDEBUG
#define LOG_TRACE                                                          \
    if (0)                                                                 \
    wb::Logger(__FILE__, __LINE__, wb::Logger::LogLevel::kTrace, __func__) \
        .stream()
#else
#define LOG_TRACE                                                          \
    if (wb::Logger::currentLevel() <= wb::Logger::LogLevel::kTrace)        \
    wb::Logger(__FILE__, __LINE__, wb::Logger::LogLevel::kTrace, __func__) \
        .stream()
#endif

#define LOG_DEBUG                                                          \
    if (wb::Logger::currentLevel() <= wb::Logger::LogLevel::kDebug)        \
    wb::Logger(__FILE__, __LINE__, wb::Logger::LogLevel::kDebug, __func__) \
        .stream()
#define LOG_INFO                                                   \
    if (wb::Logger::currentLevel() <= wb::Logger::LogLevel::kInfo) \
    wb::Logger(__FILE__, __LINE__).stream()
#define LOG_WARN \
    wb::Logger(__FILE__, __LINE__, wb::Logger::LogLevel::kWarn).stream()
#define LOG_ERROR \
    wb::Logger(__FILE__, __LINE__, wb::Logger::LogLevel::kError).stream()
#define LOG_FATAL \
    wb::Logger(__FILE__, __LINE__, wb::Logger::LogLevel::kFatal).stream()
#define LOG_SYSERR wb::Logger(__FILE__, __LINE__, true).stream()

#endif  // __LOGGER_H__